Comments (41)
Apologies in advance if this is not the best place to ask, and congrats for all the work so far.
For 1.0.0 are you guys planning adding support for fully localized routes? eg:
[{
en: "/search"
es: "/buscador"
},
{
en: "/items/[id]"
es: "/productos/[id]"
}]
(note that absence of a locale prefix in the URL path)
The reason behind this kind of routing is often SEO, which is one of the things that attracts many to Next.js in the first place. It would be great if we found a way to achieve this that also supported SSG (and the new SPR).
Thank you!
from next-translate.
A new open RFC about i18n in Next.js is open, this time from the Next.js team: vercel/next.js#17078
Version 1.0 of this library will conform 100% to the final implementation of this RFC.
from next-translate.
I just wrote a proposal to simplify i18n. Feedbacks are welcome!
from next-translate.
Great! I've played with similar ideas for a while. My thoughts:
- It's possible to get lang from router and pass to provider in _app.js. Actual language can be obtained from the query or url (asPath). Something like that:
export default function App({ Component, pageProps, router }) {
const lang = getLang(router);
return (
<LangContext.Provider value={lang}>
<Component {...pageProps} />
</LangContext.Provider>
);
}
function getLang(router) {
if (router.query.lang && availableLangs.includes(router.query.lang)) {
return router.query.lang;
}
const maybeLangSlug = router.asPath.split('/')[1];
if (availableLangs.includes(maybeLangSlug)) {
return maybeLangSlug
}
return defaultLang;
}
With this approach even static routes as pages/en/about.jsx
have lang in context without any configuration.
- There are problems with
isFallback: true
in getStaticPath. Before on-demand SSG this pages do not have lang (that's one page/[lang]/item/[id]
for every lang+id), so when user visits page for first time, he gets this common page regardless actual lang in url.
So, maybe application needs "unknown" language state for this case with isFallback
...
upd: brain-dumped ideas here — https://github.com/lastw/next-multilanguage-ssg-example (demo: https://next-multilanguage-ssg-example.now.sh/)
from next-translate.
How are you guys going about language detection?
I've got the Tutorbook site setup such that requests to non-localized URLs (e.g. /search
instead of /en/search
) are sent to an /api/redirect
serverless function that then redirects the user to the correct locale based on their Accept-Language
headers.
In next.config.js
:
rewrites() {
return [
{
// We redirect the user to their appropriate locale directory based on
// their browser request cookies (via the `/api/redirect` endpoint).
// @see {@link https://github.com/tutorbookapp/covid-tutoring/issues/35}
source: '/',
destination: '/api/redirect',
},
{
// Don't redirect if there's a locale already in the requested URL. We
// also don't redirect if the browser's just trying to fetch favicons.
// @see {@link https://github.com/UnlyEd/next-right-now/pull/42}
// @see {@link https://github.com/pillarjs/path-to-regexp/issues/223}
source: `/:locale((?!${locales.join('|')}|favicon)[^/]+)(.*)`,
destination: '/api/redirect',
},
];
},
In /pages/api/redirect.ts
:
/**
* Redirects to a locale based on the HTTP request `Accept-Language` header.
* This API endpoint is called via the experimental Next.js rewrites feature
* (see the `next.config.js` file in the root of this repository).
*
* @example `/` => `/fr`
* @example `/search` => `/de/search`
* @example `/end` => `/en/end`
*
* @see {@link https://github.com/zeit/next.js/issues/9081}
* @see {@link https://github.com/UnlyEd/next-right-now/pull/42}
* @see {@link https://github.com/tutorbookapp/covid-tutoring/issues/35}
*/
export default function redirect(
req: NextApiRequest,
res: NextApiResponse<void>
): void {
const locale: string =
parser.pick(locales, req.headers['accept-language'] || '') || defaultLocale;
console.log(`[DEBUG] Redirecting to locale (${locale})...`);
res.statusCode = 302;
res.setHeader('Location', `/${locale}${req.url}`);
res.end();
console.log(`[DEBUG] Redirected '${req.url}' to '/${locale}${req.url}'.`);
}
Once they're redirected, all Link
and Router
calls send them to the page within their locale (e.g. <Link href='/search' />
automatically becomes <Link href='/en/search' />
).
See this issue for more info on how I got that all setup.
from next-translate.
Another thing to think about @aralroca is how you'll provide automatic static optimization when adding the locale
to getStaticPaths
.
For example, let's say I want a dashboard to be at /[locale]/[username]/dashboard
but I want to statically pre-render a skeleton screen (i.e. when I don't know username
) and then fetch the user's data client-side once Next.js hydrates the page and triggers an update to the query
object (see this documentation).
But, by adding a getStaticPaths
, I'm now forced to either SSR that page or fallback to the default locale until Next.js hydrates the page. Once Next.js hydrates the page, I now know locale
and can manually fetch the necessary JSON translations. This is because we're forced by Next.js to provide all page routes when using getStaticPaths
(i.e. we can't just provide a locale
, we have to provide a locale
and a username
).
The best solution to that problem IMO is to add a helper that lets you use static optimization and then takes care of fetching static JSON translations and updating the I18nProvider
HOC once Next.js hydrates the page client-side.
from next-translate.
I hope I understood you correctly, @javiercr . But actually including the locale prefix in the URL is better for SEO. URLs without language prefixes confuse Google and are advised against.
We actually use different TLDs (domains) for each locale, and all of them point to the same Next.js app. In our case, it's the either the host (domain) or the path string (buscador
/ search
) what tell us what the current locale should be, but we also need to make sure the route for other locales returns a 404. Example:
ourapp.es/buscador => 200
ourapp.es/search => 404
ourapp.co.uk/search => 200
ourapp.co.uk/buscador => 404
Does that make sense? :)
from next-translate.
it might make sense to talk to @ijjk about this.
Whether they/he are/is already at the point, that next-translate
could use the new API in a canary
release and if there are any gotchas to be aware of.
from next-translate.
🚨 NEWS!
I adapted under 0.19.0-experimental.1
0.19.0-experimental.11
all the Next.js i18n routing. Please, if you can try the experimental version, the PR is still opened #285, if you found some issues please report it on the PR!!
I updated the examples of the repo according to adapt the breaking changes. I tried to add backwards compatibility to almost every change + console.warn
(I still have to write the guide on how to migrate)
I think this is the first step for version 1.0.0. In 0.19.0 we will still have the "build step" but much more adapted to the next.js way and it is only used to encapsulate the namespaces on each page.
from next-translate.
@aralroca I've tried running a basic example on windows. It doesn't seem to work in both development and production environments. But a without-loader example is work fine.
Environments
- OS: windows 10 v20H2
- Node: v14.15.0
- Browser: MS Edge latest version
- react: v17.0.1
- next: v1.0.3
- next-translate: v1.0.0-experimental.18
Result of without-loader example
from next-translate.
@lastw thank you a lot for your feedback!!
- It's possible to get lang from router and pass to provider in _app.js. Actual language can be obtained from the query or url (asPath).
That's a good idea to avoid to use a HOC in each page. I like it. So we can start simplifying using one HOC over _app.js
instead of one per page. This is good.
- There are problems with
isFallback: true
in getStaticPath. Before on-demand SSG this pages do not have lang (that's one page/[lang]/item/[id]
for every lang+id), so when user visits page for first time, he gets this common page regardless actual lang in url.So, maybe application needs "unknown" language state for this case with
isFallback
...
Right now I'm not understanding well the problem. I think tomorrow I'm going to experiment with it to reproduce this problem in order to understand it well.
As I understand the getStaticProps
is just delayed instead of being executed on the build. So it can't be solved by using a loading state? Or I'm wrong?
if (router.isFallback) {
return <div>Loading the page...</div>
}
I didn't understand what you mean with when user visits page for first time, he gets this common page regardless actual lang in url.
.
upd: brain-dumped ideas here — https://github.com/lastw/next-multilanguage-ssg-example (demo: https://next-multilanguage-ssg-example.now.sh/)
Thanks a lot for sharing this ❤️
from next-translate.
As I understand the getStaticProps is just delayed instead of being executed on the build. So it can't be solved by using a loading state? Or I'm wrong?
For single-language website this is solution, check router.isFallback
and render page in the loading state. Loading state can have UI around that delayed data from getStaticProps — header, footer, menu, text "loading...", etc. So, on the build phase we can build fancy and user-friendly /item/[id]
page with skeletons/spinners only where content should be.
With multiple languages on the build phase there will be generated only one page for all languages: /[lang]/item/[id]
. So, we can generate only skeletons/spinners, without text...
I didn't understand what you mean with
when user visits page for first time, he gets this common page regardless actual lang in url.
.
As for first time
I meant visiting pages (via direct link), that nobody visited before (and the generation was not triggered and cached yet). Users will get fallback page /[lang]/item/[id]
, and you need to design this page as "one fallback for all languages". Fullscreen skeleton/spinner is not so user-friendly, as it could be with specific fallback pages like /en/item/[id]
, if we had the way to make such "partial" fallbacks.
from next-translate.
I'd like to see an example of how dynamic routes would work with getServerSideProps
.
from next-translate.
I have an example page using getServerSideProps
: https://github.com/justincy/next-translate/blob/dynamic-gssp/examples/with-dynamic-routes/pages/%5Blang%5D/more-examples/get-server-side-props.js
I just need to figure out how to handle unsupported languages.
from next-translate.
I figured out how to handle unsupported languages with gSSP: justincy@2598b75#diff-bf033b67d0d5f822460e19faed094669
from next-translate.
I just opened this issue in the Next.js repository that should address the static optimization problem described above.
from next-translate.
Apologies in advance if this is not the best place to ask, and congrats for all the work so far.
For 1.0.0 are you guys planning adding support for fully localized routes? eg:
[{ en: "/search" es: "/buscador" }, { en: "/items/[id]" es: "/productos/[id]" }]
(note that absence of a locale prefix in the URL path)
The reason behind this kind of routing is often SEO, which is one of the things that attracts many to Next.js in the first place. It would be great if we found a way to achieve this that also supported SSG (and the new SPR).
Thank you!
I hope I understood you correctly, @javiercr . But actually including the locale prefix in the URL is better for SEO. URLs without language prefixes confuse Google and are advised against. Instead of subdirectories, you can use subdomains or TLDs, but relying only on cookies or accept-language headers is a bad idea. source: https://support.google.com/webmasters/answer/182192?hl=en
from next-translate.
Hi @aralroca and all,
I've been working on this i18n example: https://github.com/omarryhan/nextjs-i18n-example the past couple of days.
It builds on the work done in with-dynamic-routes example as well as other examples from the Next.js community. 1, 2.
You can read about the differences in the README. However, I want to highlight one particular pain point that I can't find a very good solution for. That is loading the translations for getStaticProps
(and getServerSideProps
). The current approach is to specify the translation namespaces you want to load in both the component (in useTranslations
) and the page (in getStaticProps
). Ideally, the translation namespaces should only be specified once, in the components. Having to define the namespaces in the pages as well is pretty annoying and makes your app prone to deployment bugs. For example, if you remove a namespace that is used by a component that you think your page will never mount. But this component conditionally mounts somewhere down the page tree. This will pass the next build command
but fail otherwise.
You might ask, why even load them in the getStaticProps
in the first placel; because that's the only place we can asynchronously load content for the pages. You can use dynamic translations as highlighted in the [language]/dynamic
page and in this repo's example as well. But loading all translations dynamically, isn't the best for SEO. And I assume most people who use Next.js have SEO as a top priority.
As a temporary workaround I wrote a function that loads all the translation JSON files found in the translations (locale) folder and passes them as props to the page. But this will significantly increase the bundle size of your app very quickly because it loads all the translations of all pages. Needless to say, it's not a very sustainable solution. I would love to hear your thoughts on that or any potential solutions. I mentioned this here, because I think this is a discussion that must be discussed for this library as well. Also, I'd prefer to use a lightweight library like this one rather than roll my own i18n logic from scratch.
from next-translate.
I just had an idea regarding the pain point I mentioned in my comment above.
Instead of either having to:
- load all the namespaces.
- manually type in all the namespaces that a page and its components are going to need.
We can export a neededTranslations
array of strings in every component, and keep exporting them until they reach all the pages that are going to render them.
e.g.
// pages/index.jsx
import FooComponent, { neededTranslations as neededTranslationsFooComponent } from './components/FooComponent/';
export default = () => (
<FooComponent />
)
export const getServerSideProps = async ({
params,
}) => ({
props: {
...await getI18nProps({
language: params?.language as string,
paths: [
...neededTranslationsFooComponent
],
}),
},
});
// components/FooComponent.jsx
import BarComponent, { neededTranslations as neededTranslationsBarComponent } from './BarComponent';
const neededTranslations = 'components/FooComponent'
export default () => {
const { t } = useTranslations(neededTranslations);
return (
<div>
foo
<BarComponent />
</div>
)
);
export const neededTranslations = [
neededTranslations,
...neededTranslationsBarComponent
]
// components/BarComponent.jsx
export default () => (
<p>
bar
</p>
)
export const neededTranslations = [
'components/BarComponent'
]
An alternative would be: Instead of exporting neededTranslations
seperately, you can just attach it to the component itself e.g.
// components/FooComponent.jsx
const Component = () => (
<p>
foo
</p>
)
Component.neededTranslations = [
'components/FooComponent'
]
export default Component;
Unfortunately Typescript won't allow you to do this, because you can't attach custom properties to the React.FC
type.
I Would love to hear your thoughts. Also, I would love to hear your thoughts about the way I am structuring the namespaces themselves. For those who didn't see the example I mentioned above, I am structuring the namespace tree identically to the way you structure your components and pages.
Here's an example
components/
Foo/
index.jsx
Bar/
index.jsx
Baz/
index.jsx
pages/
index.jsx
ssr.jsx
public/
translations/
components/
Foo/
en.json
ar.json
Bar/
en.json
ar.json
Baz/
en.json
ar.json
pages/
index/
en.json
ar.json
ssr/
en.json
ar.json
I think this way is more natural and more suited to React (and component based frameworks in general).
from next-translate.
@omarryhan maybe adding one file (i18n.json) at the component's root instead of having multiple files in a separate folder:
components/
Foo/
index.jsx
style.css
i18n.json
Bar/
index.jsx
style.css
i18n.json
Baz/
index.jsx
from next-translate.
I like that better. But where do you keep the translations for pages? Maybe in a special pageTranslations
folder in the root of the project?
from next-translate.
maybe something like this will do it, nextjs don't generate a page for i18n.json, and a (my-page/index.tsx is equivalent to my-page.tsx):
pages/
index.tsx
i18n.json
about/
index.tsx
i18n.json
[locale]/
index.tsx
i18n.json
catalog/
index.tsx
i18n.json
from next-translate.
About having one file instead of multiple, if you do that, you won't be able to tree shake the translations. So, for every page you load, you'll have to download all the translations in all languages for that particular page.
About the structure itself, I think it will get messy pretty quickly to not have the translations in a dedicated translations folder. Especially if you have more than 2 languages. On the other hand, if you put them in a translations folder, the Next.js router might create a page for it, not sure though.
Another solution is to strictly only translate components, but that's a bit prohibitive imo.
from next-translate.
Any progress on this? Would be great to be able to dynamically generate locale routes rather than using a custom build step.
Another alternative to a [lang]
dynamic parent route is generating a custom exportPathMap
like explained in vercel/next.js#5509 (comment), though getStaticPaths
might be cleaner. Either way it'd be great to offload the lang path generation to Next's buildstep with the various export/path generation hooks, rather than using our own custom one.
from next-translate.
It would be great if somebody can try version 0.19.0-experimental.6 to see how everything works in real projects. Now what I need is some feedback from people, if I see that several people can migrate their projects successfully then I will probably do the 0.19 release (That is a big step before 1.0).
I have updated the examples: https://github.com/vinissimus/next-translate/tree/i18n-routing/examples
Thanks a lot!
from next-translate.
I added a new example in the repo; without the "build step" and without the appWithI18n
, just raw 😜
https://github.com/vinissimus/next-translate/tree/master/examples/without-build-step
I think it can also be a good alternative, although it requires a little more configuration it's not a big headache. However, as I understood, the Next.js the next step they will do after i18n routing will be to load translations, so maybe for now it's worth keeping the "build step" because it's the simplest way, and when this will be done, we will release the 1.0.0 without the build step.
from next-translate.
Proposal: #303
from next-translate.
About this proposal: If someone wants to experiment, I did a first experimental version under 1.0.0-experimental.11 prerelease.1.0.0-experimental.1
This experimental version has:
- Hotreloading/refresh files
- Faster build
- Better development experience
- De-duping logic. Right now there are some alternatives: with builder, without builder + appWithI18n, etc. This is now integrated, to skip compiling namespaces you can pass
loader: false
on thei18n.json
. - Working in the same
pages
directory than Next.js. No need of workarounds. - Easy to test
- Easy to migrate in the future
- No need to indicate to the Next.js config the
locales
anddefaultLocales
onnext.config.js
, this will be injected by the plugin. So you only will have ai18n.js
ori18n.json
root file.
Things are still missing, so several things may still fail, but I prefer you to experiment it, and if you find something, report it here (please don't do PR yet to that branch).
from next-translate.
https://github.com/vinissimus/next-translate/blob/master/src/I18nProvider.js#L80
Using router in parent component and inside Provider. I think, it need remove and make require props from parent on the contrary, don't using props
from next-translate.
@popuguytheparrot can you explain it a little bit further, maybe with an example. Sorry but I didn't understand It.
from next-translate.
Call useRouter in parent(_app) and call again in I18nProvider.
It seems to me that the logic is duplicated
export default function MyApp({ Component, pageProps }) {
const router = useRouter();
return (
<I18nProvider lang={router.locale} namespaces={pageProps._ns}>
<Component {...pageProps} />
</I18nProvider>
</>
);
}
export default function I18nProvider({
lang: lng,
namespaces = {},
children,
logger = missingKeyLogger,
}) {
const { locale } = useRouter() || {}
from next-translate.
Ah @popuguytheparrot , you say that because of the example in the repo: https://github.com/vinissimus/next-translate/blob/master/examples/without-build-step/pages/_app.js It's funny, I encourage you to try the example and eliminate the "lang" of the _app.js
, you'll see that it doesn't work. It's like that inside the I18nProvider
the router only works in CSR, and in SSR always gives undefined
, instead, in _app.js
it works both CSR and SSR. I do not understand the reason...
However, in the I18nProvider
is interesting to keep both (from props or router.locale as fallback), because it will not always be the locale of the router, as it is possible for example to make the same page has 2 languages using two I18nProvider. Explained here: https://github.com/vinissimus/next-translate#10-how-to-use-multi-language-in-a-page
from next-translate.
The migration guide to 1.0.0 is still available on https://github.com/vinissimus/next-translate/blob/1.0.0-experimental/docs/migration-guide-1.0.0.md (replacing the version to the latest experimental prerelease it should work).
Everything is already quite stabilized, in a short time we will surely go from the experimental version to the canary, since it looks like we will finally evolve around here. And I hope that the final version 1.0.0 will be available in December.
It would help a lot if you could try the latest prerelease and report possible problems; things in the documentation, issues... 🙏
from next-translate.
Can someone who has windows confirm that it works well in the development environment?
from next-translate.
is it possible to type locales with this feature?
https://devblogs.microsoft.com/typescript/announcing-typescript-4-1-beta/#template-literal-types
from next-translate.
is it possible to type locales with this feature?
https://devblogs.microsoft.com/typescript/announcing-typescript-4-1-beta/#template-literal-types
Yes @popuguytheparrot . And feel free to PR. I don't really have much experience with TypeScript and I'm sure you can do much better than I.
from next-translate.
I did a short demo video about 1.0:
- Demo video: https://www.youtube.com/watch?v=QnCIjjYLCfc
- Code: https://github.com/vinissimus/next-translate/tree/1.0.0-experimental
I hope you find this useful 😊
(sorry my English)
from next-translate.
It doesn't work with nextjs 10 and SSG (static export). Error: i18n support is not compatible with next export
. Don't think it's possible to make use of the built-in i18n feature this way.
from next-translate.
@vimutti77 Thanks to report It! It looks like it is the fault of the absolute import on the default loadLocaleFrom
, which has a mixture of / and \ :
https://github.com/vinissimus/next-translate/runs/1457587510
defaultLoader: (l, n) =>
import(`D:\a\next-translate\next-translate/locales/${l}/${n}`).then(
(m) => m.default
),
Do you know how this import would be for it to be valid?
I suppose that fixing the string would work already. Or another solution will be using an alias in webpack instead. When I have some time I fix it, I already asked if someone could look at it in Windows because I was afraid it would not work after see the failing test.
If you add the loadLocaleFrom
in the configuration, so that it does not use the default, it works?
Thank you very much
from next-translate.
@aralroca This is what I got when run yarn build
on basic example of next-translate v1.0.0-experimental.18
.
$ next build
info - Creating an optimized production build
info - Compiled successfully
info - Collecting page data
[=== ] info - Generating static pages (0/24){ lang: '' }
[ ===] info - Generating static pages (0/24){ lang: '' }
{ query: {} }
{ lang: '' }
{ lang: '' }
[ ==] info - Generating static pages (6/24){ query: { slug: '[slug]' } }
{ query: { slug: '[slug]' } }
{ query: { slug: '[slug]' } }
info - Generating static pages (24/24)
info - Finalizing page optimization
Page Size First Load JS
┌ ○ / 360 B 68.4 kB
├ ○ /404 677 B 64.8 kB
├ ○ /more-examples 2.2 kB 70.3 kB
├ ○ /more-examples/catchall/[...all] 2.05 kB 66.2 kB
├ ○ /more-examples/dynamic-namespace 2.54 kB 66.7 kB
└ ○ /more-examples/dynamicroute/[slug] 2.08 kB 66.2 kB
+ First Load JS shared by all 64.1 kB
├ chunks/45dce98e7af2a80bb0901cbf74b9d0d8c7943397.85a7f9.js 13.1 kB
├ chunks/framework.a4a060.js 41.8 kB
├ chunks/main.2784a9.js 7.25 kB
├ chunks/pages/_app.78c12f.js 1.28 kB
└ chunks/webpack.e06743.js 751 B
λ (Server) server-side renders at runtime (uses getInitialProps or getServerSideProps)
○ (Static) automatically rendered as static HTML (uses no initial props)
● (SSG) automatically generated as static HTML + JSON (uses getStaticProps)
(ISR) incremental static regeneration (uses revalidate in getStaticProps)
from next-translate.
Closing this. Already fixed on 1.0.0-canary.1. The 1.0 will be released approximately 1-2 weeks. 😊
from next-translate.
Related Issues (20)
- Localizations not working in prod bundle on NextJs 14 HOT 1
- Buildng project using only app router HOT 9
- Translation not working on client component inside layout HOT 10
- Warning: Detected multiple renderers concurrently rendering the same context provider.
- 404 when accssing homepage HOT 7
- 404 on root when using baseURL in vercel HOT 1
- Can't resolve 'next-translate/appWithI18n' on 2.7.0-canary.1 HOT 6
- Cold Start on dynamic routes HOT 5
- Type safety for getT function
- Regular locale naming like "en" breaks the i18n config HOT 1
- Cannot read properties of undefined (reading 'localesToIgnore')
- Docs reference incorrect format/formatter options HOT 1
- App router for domain based local issue HOT 1
- lang useTranslation hook not refreshing in sitemap page HOT 3
- Big loading times and scroll to top once loaded HOT 2
- t is unstable in appDir HOT 1
- Issue & Update on how to use the library with app router HOT 1
- Translations not being loaded when running development build (NextJS 14.1.4) HOT 1
- Change language - compatibility with Next 13
- the demo example is not working
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from next-translate.