[{"data":1,"prerenderedAt":1512},["ShallowReactive",2],{"blog-\u002Fblog\u002Fbeing-heard":3},{"id":4,"title":5,"body":6,"canonical":1500,"date":1501,"description":1502,"draft":1503,"extension":1504,"lang":1505,"meta":1506,"navigation":325,"path":1507,"readingTime":1508,"seo":1509,"stem":1510,"__hash__":1511},"blog\u002Fblog\u002Fbeing-heard.md","Being Heard Isn't Just About Speaking — It's About Understanding",{"type":7,"value":8,"toc":1481},"minimark",[9,14,22,25,37,40,43,46,49,69,74,77,83,87,94,112,116,119,130,133,137,140,143,167,170,177,181,187,193,197,200,207,268,272,275,282,375,379,382,389,498,505,587,591,594,677,681,684,691,909,913,916,1189,1193,1196,1201,1356,1361,1445,1449,1456,1464,1468,1471,1474,1477],[10,11,13],"h3",{"id":12},"from-human-connection-to-multilingual-ux-using-nextjs-and-next-intl-to-build-user-aware-language-ready-web-experiences","From human connection to multilingual UX — using Next.js and next-intl to build user-aware, language-ready web experiences.",[15,16,17],"p",{},[18,19],"img",{"alt":20,"src":21},"","https:\u002F\u002Fcdn-images-1.medium.com\u002Fmax\u002F730\u002F1*MlejWnprLa0VZpKGNdt9vg.png",[15,23,24],{},"I've always loved to be heard — not just in the sense of speaking, but in a way that makes me feel understood. We all do. It's human nature. But more often than not, I've found myself in conversations where I wasn't truly listened to. I was acknowledged, but not understood.",[15,26,27,28,32,33,36],{},"There was a time when I kept explaining myself over and over, hoping the other person would get it. But they were listening through their own filter — hearing what they wanted, not what I was actually saying. And I realized something: Most of us communicate the way ",[29,30,31],"em",{},"we"," want to be heard, instead of listening the way ",[29,34,35],{},"someone else"," wants to be understood.",[15,38,39],{},"But what if we chose to listen differently? What if, instead of assuming, we asked? What if we designed experiences that didn't just express what we wanted to say, but adapted to the way others understood best?",[15,41,42],{},"Just like in personal conversations, the way we design digital experiences determines whether users feel understood. Imagine visiting a website that doesn't support your language. You might understand bits and pieces, but the experience feels foreign, even alienating.",[15,44,45],{},"So, what does it really mean to listen? It's more than just acknowledging the presence of another — it's about making them feel at home, like they belong. In the digital world, listening means adapting to the user's needs, not expecting them to adapt to us.",[15,47,48],{},"Think about it: When you walk into a space where everything is unfamiliar — the language, the signs, the way things work — it takes extra effort just to navigate. You feel like an outsider. But when that same space acknowledges your language, your culture, and your way of thinking, suddenly, it feels welcoming. It feels like it was made for you.",[15,50,51,52,59,60,68],{},"That's why ",[53,54,58],"a",{"href":55,"rel":56},"https:\u002F\u002Fwww.techtarget.com\u002Fwhatis\u002Fdefinition\u002Finternationalization-I18N",[57],"nofollow","internationalization (i18n)"," is so powerful. It's not just about translating words — it's about creating an experience that listens to the user, understands them, and speaks to them in a way that feels natural. And for Next.js developers, one of the best ways to achieve this is through ",[53,61,64],{"href":62,"rel":63},"https:\u002F\u002Fnext-intl.dev\u002Fdocs\u002Fgetting-started",[57],[65,66,67],"strong",{},"next-intl",".",[70,71,73],"h2",{"id":72},"how-do-we-start-listening","How Do We Start Listening?",[15,75,76],{},"Just like in any conversation, there are different ways to listen. Sometimes, we listen through direct engagement — asking questions, responding in real time. Other times, we listen passively — observing patterns, understanding behavior, and adapting accordingly.",[15,78,79,80,82],{},"With ",[65,81,67],{},", we have similar choices when it comes to internationalization. We can structure our applications in a way that prioritizes adaptability, ensuring users feel like the experience was crafted for them. In Next.js, this comes down to two key approaches:",[10,84,86],{"id":85},"with-i18n-routing-letting-the-url-speak","With i18n Routing → Letting the URL Speak",[15,88,89,90,93],{},"This approach integrates the language directly into the app's navigation. Just like entering a space where the signs immediately tell you, ",[29,91,92],{},"\"We speak your language,\""," this method ensures users instantly see content in their preferred language.",[95,96,97,106,109],"ul",{},[98,99,100,101,105],"li",{},"Uses a top-level ",[102,103,104],"span",{},"locale"," segment for prefixed pathnames, such as \u002Fen\u002Fabout or \u002Fes\u002Fcontact.",[98,107,108],{},"Supports domain-based routing, like en.example.com\u002Fabout for region-specific experiences.",[98,110,111],{},"Ideal for applications that need clear and structured multilingual navigation.",[10,113,115],{"id":114},"without-i18n-routing-listening-to-user-preferences","Without i18n Routing → Listening to User Preferences",[15,117,118],{},"Not every conversation starts with words; sometimes, we listen by observing. This approach allows applications to detect and adapt to a user's language preference dynamically.",[95,120,121,124,127],{},[98,122,123],{},"Useful for apps that rely on user settings to determine language.",[98,125,126],{},"Works well for single-language applications that may need flexibility in the future.",[98,128,129],{},"Keeps URLs clean and language-independent, shifting the focus to user choice rather than URL structure.",[15,131,132],{},"Both methods have their place, just like different ways of listening in conversation. The key is to choose the one that best fits the needs of your users — because in the end, it's not just about what we build, but how we make people feel understood.",[70,134,136],{"id":135},"getting-started-learning-to-listen-through-code","Getting Started: Learning to Listen Through Code",[15,138,139],{},"Just like in any meaningful conversation, listening starts with intention. In the world of applications, that intention shows up when we choose to support multiple languages — when we decide that users from anywhere should feel like the experience was designed with them in mind.",[15,141,142],{},"With Next.js and next-intl, that journey begins simply:",[144,145,149],"pre",{"className":146,"code":147,"language":148,"meta":20,"style":20},"language-bash shiki shiki-themes github-light github-dark","npm install next-intl\n","bash",[150,151,152],"code",{"__ignoreMap":20},[102,153,156,160,164],{"class":154,"line":155},"line",1,[102,157,159],{"class":158},"sScJk","npm",[102,161,163],{"class":162},"sZZnC"," install",[102,165,166],{"class":162}," next-intl\n",[15,168,169],{},"But just like human conversations, how we listen matters just as much as what we hear. Next-intl gives us two ways to approach internationalization: one that makes language visible, and another that quietly adapts based on the user's preferences.",[15,171,172,173,176],{},"Let's begin with the first: ",[65,174,175],{},"i18n Routing"," — a way to let language speak through the URL itself.",[70,178,180],{"id":179},"with-i18n-routing-making-language-part-of-the-experience","With i18n Routing: Making Language Part of the Experience",[15,182,183,184],{},"When users land on a page and immediately see content in their language — like \u002Fen\u002Fabout or \u002Fes\u002Fcontact—they know: ",[29,185,186],{},"\"This space speaks to me.\"",[15,188,189,190,192],{},"This approach brings language into the very fabric of navigation, using a top-level ",[102,191,104],{}," segment in your Next.js routes. Setting it up is easier than it sounds.",[10,194,196],{"id":195},"step-1-define-your-messages","Step 1: Define Your Messages",[15,198,199],{},"Each language gets its own JSON file. Here's a simple example:",[15,201,202],{},[29,203,204],{},[65,205,206],{},"messages\u002Fen.json:",[144,208,212],{"className":209,"code":210,"language":211,"meta":20,"style":20},"language-json shiki shiki-themes github-light github-dark","{\n  \"HomePage\": {\n    \"title\": \"Hello world!\",\n    \"about\": \"Go to the about page\"\n  }\n}\n","json",[150,213,214,220,230,245,256,262],{"__ignoreMap":20},[102,215,216],{"class":154,"line":155},[102,217,219],{"class":218},"sVt8B","{\n",[102,221,223,227],{"class":154,"line":222},2,[102,224,226],{"class":225},"sj4cs","  \"HomePage\"",[102,228,229],{"class":218},": {\n",[102,231,233,236,239,242],{"class":154,"line":232},3,[102,234,235],{"class":225},"    \"title\"",[102,237,238],{"class":218},": ",[102,240,241],{"class":162},"\"Hello world!\"",[102,243,244],{"class":218},",\n",[102,246,248,251,253],{"class":154,"line":247},4,[102,249,250],{"class":225},"    \"about\"",[102,252,238],{"class":218},[102,254,255],{"class":162},"\"Go to the about page\"\n",[102,257,259],{"class":154,"line":258},5,[102,260,261],{"class":218},"  }\n",[102,263,265],{"class":154,"line":264},6,[102,266,267],{"class":218},"}\n",[10,269,271],{"id":270},"step-2-configure-nextjs-for-locales","Step 2: Configure Next.js for Locales",[15,273,274],{},"With next-intl, we wrap our config to enable locale-based routing.",[15,276,277],{},[29,278,279],{},[65,280,281],{},"next.config.ts",[144,283,287],{"className":284,"code":285,"language":286,"meta":20,"style":20},"language-ts shiki shiki-themes github-light github-dark","import { NextConfig } from 'next';\nimport createNextIntlPlugin from 'next-intl\u002Fplugin';\n\nconst nextConfig: NextConfig = {};\nconst withNextIntl = createNextIntlPlugin();\nexport default withNextIntl(nextConfig);\n","ts",[150,288,289,307,321,327,347,362],{"__ignoreMap":20},[102,290,291,295,298,301,304],{"class":154,"line":155},[102,292,294],{"class":293},"szBVR","import",[102,296,297],{"class":218}," { NextConfig } ",[102,299,300],{"class":293},"from",[102,302,303],{"class":162}," 'next'",[102,305,306],{"class":218},";\n",[102,308,309,311,314,316,319],{"class":154,"line":222},[102,310,294],{"class":293},[102,312,313],{"class":218}," createNextIntlPlugin ",[102,315,300],{"class":293},[102,317,318],{"class":162}," 'next-intl\u002Fplugin'",[102,320,306],{"class":218},[102,322,323],{"class":154,"line":232},[102,324,326],{"emptyLinePlaceholder":325},true,"\n",[102,328,329,332,335,338,341,344],{"class":154,"line":247},[102,330,331],{"class":293},"const",[102,333,334],{"class":225}," nextConfig",[102,336,337],{"class":293},":",[102,339,340],{"class":158}," NextConfig",[102,342,343],{"class":293}," =",[102,345,346],{"class":218}," {};\n",[102,348,349,351,354,356,359],{"class":154,"line":258},[102,350,331],{"class":293},[102,352,353],{"class":225}," withNextIntl",[102,355,343],{"class":293},[102,357,358],{"class":158}," createNextIntlPlugin",[102,360,361],{"class":218},"();\n",[102,363,364,367,370,372],{"class":154,"line":264},[102,365,366],{"class":293},"export",[102,368,369],{"class":293}," default",[102,371,353],{"class":158},[102,373,374],{"class":218},"(nextConfig);\n",[10,376,378],{"id":377},"step-3-define-routing-and-navigation-behavior","Step 3: Define Routing and Navigation Behavior",[15,380,381],{},"Tell your app what languages it understands, and what to fall back on if it doesn't.",[15,383,384],{},[29,385,386],{},[65,387,388],{},"src\u002Fi18n\u002Frouting.ts",[144,390,392],{"className":284,"code":391,"language":286,"meta":20,"style":20},"import { defineRouting } from 'next-intl\u002Frouting';\nimport { createNavigation } from 'next-intl\u002Fnavigation';\n\nexport const routing = defineRouting({\n  \u002F\u002F list of all locales that are supported\n  locales: ['en', 'es', 'kr'],\n\n  \u002F\u002F Used when no locale matches, fallback to default\n  defaultLocale: 'en'\n});\n",[150,393,394,408,422,426,444,450,472,477,483,492],{"__ignoreMap":20},[102,395,396,398,401,403,406],{"class":154,"line":155},[102,397,294],{"class":293},[102,399,400],{"class":218}," { defineRouting } ",[102,402,300],{"class":293},[102,404,405],{"class":162}," 'next-intl\u002Frouting'",[102,407,306],{"class":218},[102,409,410,412,415,417,420],{"class":154,"line":222},[102,411,294],{"class":293},[102,413,414],{"class":218}," { createNavigation } ",[102,416,300],{"class":293},[102,418,419],{"class":162}," 'next-intl\u002Fnavigation'",[102,421,306],{"class":218},[102,423,424],{"class":154,"line":232},[102,425,326],{"emptyLinePlaceholder":325},[102,427,428,430,433,436,438,441],{"class":154,"line":247},[102,429,366],{"class":293},[102,431,432],{"class":293}," const",[102,434,435],{"class":225}," routing",[102,437,343],{"class":293},[102,439,440],{"class":158}," defineRouting",[102,442,443],{"class":218},"({\n",[102,445,446],{"class":154,"line":258},[102,447,449],{"class":448},"sJ8bj","  \u002F\u002F list of all locales that are supported\n",[102,451,452,455,458,461,464,466,469],{"class":154,"line":264},[102,453,454],{"class":218},"  locales: [",[102,456,457],{"class":162},"'en'",[102,459,460],{"class":218},", ",[102,462,463],{"class":162},"'es'",[102,465,460],{"class":218},[102,467,468],{"class":162},"'kr'",[102,470,471],{"class":218},"],\n",[102,473,475],{"class":154,"line":474},7,[102,476,326],{"emptyLinePlaceholder":325},[102,478,480],{"class":154,"line":479},8,[102,481,482],{"class":448},"  \u002F\u002F Used when no locale matches, fallback to default\n",[102,484,486,489],{"class":154,"line":485},9,[102,487,488],{"class":218},"  defaultLocale: ",[102,490,491],{"class":162},"'en'\n",[102,493,495],{"class":154,"line":494},10,[102,496,497],{"class":218},"});\n",[15,499,500],{},[29,501,502],{},[65,503,504],{},"src\u002Fi18n\u002Fnavigation.ts",[144,506,508],{"className":284,"code":507,"language":286,"meta":20,"style":20},"import {createNavigation} from 'next-intl\u002Fnavigation';\nimport {routing} from '.\u002Frouting';\n\nexport const {Link, redirect, usePathname, useRouter, getPathname} =\n  createNavigation(routing);\n",[150,509,510,523,537,541,579],{"__ignoreMap":20},[102,511,512,514,517,519,521],{"class":154,"line":155},[102,513,294],{"class":293},[102,515,516],{"class":218}," {createNavigation} ",[102,518,300],{"class":293},[102,520,419],{"class":162},[102,522,306],{"class":218},[102,524,525,527,530,532,535],{"class":154,"line":222},[102,526,294],{"class":293},[102,528,529],{"class":218}," {routing} ",[102,531,300],{"class":293},[102,533,534],{"class":162}," '.\u002Frouting'",[102,536,306],{"class":218},[102,538,539],{"class":154,"line":232},[102,540,326],{"emptyLinePlaceholder":325},[102,542,543,545,547,550,553,555,558,560,563,565,568,570,573,576],{"class":154,"line":247},[102,544,366],{"class":293},[102,546,432],{"class":293},[102,548,549],{"class":218}," {",[102,551,552],{"class":225},"Link",[102,554,460],{"class":218},[102,556,557],{"class":225},"redirect",[102,559,460],{"class":218},[102,561,562],{"class":225},"usePathname",[102,564,460],{"class":218},[102,566,567],{"class":225},"useRouter",[102,569,460],{"class":218},[102,571,572],{"class":225},"getPathname",[102,574,575],{"class":218},"} ",[102,577,578],{"class":293},"=\n",[102,580,581,584],{"class":154,"line":258},[102,582,583],{"class":158},"  createNavigation",[102,585,586],{"class":218},"(routing);\n",[10,588,590],{"id":589},"step-4-middleware-that-adapts-like-a-good-listener","Step 4: Middleware That Adapts Like a Good Listener",[15,592,593],{},"Middleware lets your app greet users in their language from the very first request.",[144,595,597],{"className":284,"code":596,"language":286,"meta":20,"style":20},"import createMiddleware from \"next-intl\u002Fmiddleware\";\nimport { routing } from \".\u002Fi18n\u002Frouting\";\n\nexport default createMiddleware(routing);\nexport const config = {\n matcher: [\"\u002F((?!_next|api|ingest|.*\\\\..*).*)\"],\n};\n",[150,598,599,613,627,631,642,656,672],{"__ignoreMap":20},[102,600,601,603,606,608,611],{"class":154,"line":155},[102,602,294],{"class":293},[102,604,605],{"class":218}," createMiddleware ",[102,607,300],{"class":293},[102,609,610],{"class":162}," \"next-intl\u002Fmiddleware\"",[102,612,306],{"class":218},[102,614,615,617,620,622,625],{"class":154,"line":222},[102,616,294],{"class":293},[102,618,619],{"class":218}," { routing } ",[102,621,300],{"class":293},[102,623,624],{"class":162}," \".\u002Fi18n\u002Frouting\"",[102,626,306],{"class":218},[102,628,629],{"class":154,"line":232},[102,630,326],{"emptyLinePlaceholder":325},[102,632,633,635,637,640],{"class":154,"line":247},[102,634,366],{"class":293},[102,636,369],{"class":293},[102,638,639],{"class":158}," createMiddleware",[102,641,586],{"class":218},[102,643,644,646,648,651,653],{"class":154,"line":258},[102,645,366],{"class":293},[102,647,432],{"class":293},[102,649,650],{"class":225}," config",[102,652,343],{"class":293},[102,654,655],{"class":218}," {\n",[102,657,658,661,664,667,670],{"class":154,"line":264},[102,659,660],{"class":218}," matcher: [",[102,662,663],{"class":162},"\"\u002F((?!_next|api|ingest|.*",[102,665,666],{"class":225},"\\\\",[102,668,669],{"class":162},"..*).*)\"",[102,671,471],{"class":218},[102,673,674],{"class":154,"line":474},[102,675,676],{"class":218},"};\n",[10,678,680],{"id":679},"step-5-responding-based-on-locale-context","Step 5: Responding Based on Locale Context",[15,682,683],{},"Server components need to know what language they're speaking. That's where request.ts comes in.",[15,685,686],{},[29,687,688],{},[65,689,690],{},"src\u002Fi18n\u002Frequest.ts",[144,692,694],{"className":284,"code":693,"language":286,"meta":20,"style":20},"import type { Locale } from \"@\u002Fmessages\u002F_schema\";\nimport { getRequestConfig } from \"next-intl\u002Fserver\";\nimport { routing } from \".\u002Frouting\";\n\nexport default getRequestConfig(async ({ requestLocale }) => {\n \u002F\u002F This typically corresponds to the `[locale]` segment\n let locale = await requestLocale;\n\n if (!locale || !routing.locales.includes(locale as Locale)) {\n  locale = routing.defaultLocale;\n }\n\nreturn {\n  locale,\n  messages: (await import(`..\u002Fmessages\u002F${locale}.json`)).default,\n };\n});\n",[150,695,696,713,727,740,744,774,779,796,800,838,848,854,859,867,873,898,904],{"__ignoreMap":20},[102,697,698,700,703,706,708,711],{"class":154,"line":155},[102,699,294],{"class":293},[102,701,702],{"class":293}," type",[102,704,705],{"class":218}," { Locale } ",[102,707,300],{"class":293},[102,709,710],{"class":162}," \"@\u002Fmessages\u002F_schema\"",[102,712,306],{"class":218},[102,714,715,717,720,722,725],{"class":154,"line":222},[102,716,294],{"class":293},[102,718,719],{"class":218}," { getRequestConfig } ",[102,721,300],{"class":293},[102,723,724],{"class":162}," \"next-intl\u002Fserver\"",[102,726,306],{"class":218},[102,728,729,731,733,735,738],{"class":154,"line":232},[102,730,294],{"class":293},[102,732,619],{"class":218},[102,734,300],{"class":293},[102,736,737],{"class":162}," \".\u002Frouting\"",[102,739,306],{"class":218},[102,741,742],{"class":154,"line":247},[102,743,326],{"emptyLinePlaceholder":325},[102,745,746,748,750,753,756,759,762,766,769,772],{"class":154,"line":258},[102,747,366],{"class":293},[102,749,369],{"class":293},[102,751,752],{"class":158}," getRequestConfig",[102,754,755],{"class":218},"(",[102,757,758],{"class":293},"async",[102,760,761],{"class":218}," ({ ",[102,763,765],{"class":764},"s4XuR","requestLocale",[102,767,768],{"class":218}," }) ",[102,770,771],{"class":293},"=>",[102,773,655],{"class":218},[102,775,776],{"class":154,"line":264},[102,777,778],{"class":448}," \u002F\u002F This typically corresponds to the `[locale]` segment\n",[102,780,781,784,787,790,793],{"class":154,"line":474},[102,782,783],{"class":293}," let",[102,785,786],{"class":218}," locale ",[102,788,789],{"class":293},"=",[102,791,792],{"class":293}," await",[102,794,795],{"class":218}," requestLocale;\n",[102,797,798],{"class":154,"line":479},[102,799,326],{"emptyLinePlaceholder":325},[102,801,802,805,808,811,814,817,820,823,826,829,832,835],{"class":154,"line":485},[102,803,804],{"class":293}," if",[102,806,807],{"class":218}," (",[102,809,810],{"class":293},"!",[102,812,813],{"class":218},"locale ",[102,815,816],{"class":293},"||",[102,818,819],{"class":293}," !",[102,821,822],{"class":218},"routing.locales.",[102,824,825],{"class":158},"includes",[102,827,828],{"class":218},"(locale ",[102,830,831],{"class":293},"as",[102,833,834],{"class":158}," Locale",[102,836,837],{"class":218},")) {\n",[102,839,840,843,845],{"class":154,"line":494},[102,841,842],{"class":218},"  locale ",[102,844,789],{"class":293},[102,846,847],{"class":218}," routing.defaultLocale;\n",[102,849,851],{"class":154,"line":850},11,[102,852,853],{"class":218}," }\n",[102,855,857],{"class":154,"line":856},12,[102,858,326],{"emptyLinePlaceholder":325},[102,860,862,865],{"class":154,"line":861},13,[102,863,864],{"class":293},"return",[102,866,655],{"class":218},[102,868,870],{"class":154,"line":869},14,[102,871,872],{"class":218},"  locale,\n",[102,874,876,879,882,885,887,890,892,895],{"class":154,"line":875},15,[102,877,878],{"class":218},"  messages: (",[102,880,881],{"class":293},"await",[102,883,884],{"class":293}," import",[102,886,755],{"class":218},[102,888,889],{"class":162},"`..\u002Fmessages\u002F${",[102,891,104],{"class":218},[102,893,894],{"class":162},"}.json`",[102,896,897],{"class":218},")).default,\n",[102,899,901],{"class":154,"line":900},16,[102,902,903],{"class":218}," };\n",[102,905,907],{"class":154,"line":906},17,[102,908,497],{"class":218},[10,910,912],{"id":911},"step-6-set-up-your-locale-layout","Step 6: Set Up Your Locale Layout",[15,914,915],{},"Now we make sure every page honors the selected language.",[144,917,921],{"className":918,"code":919,"language":920,"meta":20,"style":20},"language-tsx shiki shiki-themes github-light github-dark","import {NextIntlClientProvider, hasLocale} from 'next-intl';\nimport {notFound} from 'next\u002Fnavigation';\nimport {routing} from '@\u002Fi18n\u002Frouting';\n\nexport default async function LocaleLayout({\n  children,\n  params\n}: {\n  children: React.ReactNode;\n  params: Promise\u003C{locale: string}>;\n}) {\n  \u002F\u002F Ensure that the incoming `locale` is valid\n  const {locale} = await params;\n  if (!hasLocale(routing.locales, locale)) {\n    notFound();\n  }\n\n  return (\n    \u003Chtml lang={locale}>\n      \u003Cbody>\n        \u003CNextIntlClientProvider>{children}\u003C\u002FNextIntlClientProvider>\n      \u003C\u002Fbody>\n    \u003C\u002Fhtml>\n  );\n}\n","tsx",[150,922,923,937,951,964,968,985,992,997,1006,1022,1045,1050,1055,1073,1088,1095,1099,1103,1112,1130,1142,1158,1168,1178,1184],{"__ignoreMap":20},[102,924,925,927,930,932,935],{"class":154,"line":155},[102,926,294],{"class":293},[102,928,929],{"class":218}," {NextIntlClientProvider, hasLocale} ",[102,931,300],{"class":293},[102,933,934],{"class":162}," 'next-intl'",[102,936,306],{"class":218},[102,938,939,941,944,946,949],{"class":154,"line":222},[102,940,294],{"class":293},[102,942,943],{"class":218}," {notFound} ",[102,945,300],{"class":293},[102,947,948],{"class":162}," 'next\u002Fnavigation'",[102,950,306],{"class":218},[102,952,953,955,957,959,962],{"class":154,"line":232},[102,954,294],{"class":293},[102,956,529],{"class":218},[102,958,300],{"class":293},[102,960,961],{"class":162}," '@\u002Fi18n\u002Frouting'",[102,963,306],{"class":218},[102,965,966],{"class":154,"line":247},[102,967,326],{"emptyLinePlaceholder":325},[102,969,970,972,974,977,980,983],{"class":154,"line":258},[102,971,366],{"class":293},[102,973,369],{"class":293},[102,975,976],{"class":293}," async",[102,978,979],{"class":293}," function",[102,981,982],{"class":158}," LocaleLayout",[102,984,443],{"class":218},[102,986,987,990],{"class":154,"line":264},[102,988,989],{"class":764},"  children",[102,991,244],{"class":218},[102,993,994],{"class":154,"line":474},[102,995,996],{"class":764},"  params\n",[102,998,999,1002,1004],{"class":154,"line":479},[102,1000,1001],{"class":218},"}",[102,1003,337],{"class":293},[102,1005,655],{"class":218},[102,1007,1008,1010,1012,1015,1017,1020],{"class":154,"line":485},[102,1009,989],{"class":764},[102,1011,337],{"class":293},[102,1013,1014],{"class":158}," React",[102,1016,68],{"class":218},[102,1018,1019],{"class":158},"ReactNode",[102,1021,306],{"class":218},[102,1023,1024,1027,1029,1032,1035,1037,1039,1042],{"class":154,"line":494},[102,1025,1026],{"class":764},"  params",[102,1028,337],{"class":293},[102,1030,1031],{"class":158}," Promise",[102,1033,1034],{"class":218},"\u003C{",[102,1036,104],{"class":764},[102,1038,337],{"class":293},[102,1040,1041],{"class":225}," string",[102,1043,1044],{"class":218},"}>;\n",[102,1046,1047],{"class":154,"line":850},[102,1048,1049],{"class":218},"}) {\n",[102,1051,1052],{"class":154,"line":856},[102,1053,1054],{"class":448},"  \u002F\u002F Ensure that the incoming `locale` is valid\n",[102,1056,1057,1060,1062,1064,1066,1068,1070],{"class":154,"line":861},[102,1058,1059],{"class":293},"  const",[102,1061,549],{"class":218},[102,1063,104],{"class":225},[102,1065,575],{"class":218},[102,1067,789],{"class":293},[102,1069,792],{"class":293},[102,1071,1072],{"class":218}," params;\n",[102,1074,1075,1078,1080,1082,1085],{"class":154,"line":869},[102,1076,1077],{"class":293},"  if",[102,1079,807],{"class":218},[102,1081,810],{"class":293},[102,1083,1084],{"class":158},"hasLocale",[102,1086,1087],{"class":218},"(routing.locales, locale)) {\n",[102,1089,1090,1093],{"class":154,"line":875},[102,1091,1092],{"class":158},"    notFound",[102,1094,361],{"class":218},[102,1096,1097],{"class":154,"line":900},[102,1098,261],{"class":218},[102,1100,1101],{"class":154,"line":906},[102,1102,326],{"emptyLinePlaceholder":325},[102,1104,1106,1109],{"class":154,"line":1105},18,[102,1107,1108],{"class":293},"  return",[102,1110,1111],{"class":218}," (\n",[102,1113,1115,1118,1122,1125,1127],{"class":154,"line":1114},19,[102,1116,1117],{"class":218},"    \u003C",[102,1119,1121],{"class":1120},"s9eBZ","html",[102,1123,1124],{"class":158}," lang",[102,1126,789],{"class":293},[102,1128,1129],{"class":218},"{locale}>\n",[102,1131,1133,1136,1139],{"class":154,"line":1132},20,[102,1134,1135],{"class":218},"      \u003C",[102,1137,1138],{"class":1120},"body",[102,1140,1141],{"class":218},">\n",[102,1143,1145,1148,1151,1154,1156],{"class":154,"line":1144},21,[102,1146,1147],{"class":218},"        \u003C",[102,1149,1150],{"class":225},"NextIntlClientProvider",[102,1152,1153],{"class":218},">{children}\u003C\u002F",[102,1155,1150],{"class":225},[102,1157,1141],{"class":218},[102,1159,1161,1164,1166],{"class":154,"line":1160},22,[102,1162,1163],{"class":218},"      \u003C\u002F",[102,1165,1138],{"class":1120},[102,1167,1141],{"class":218},[102,1169,1171,1174,1176],{"class":154,"line":1170},23,[102,1172,1173],{"class":218},"    \u003C\u002F",[102,1175,1121],{"class":1120},[102,1177,1141],{"class":218},[102,1179,1181],{"class":154,"line":1180},24,[102,1182,1183],{"class":218},"  );\n",[102,1185,1187],{"class":154,"line":1186},25,[102,1188,267],{"class":218},[10,1190,1192],{"id":1191},"step-7-let-your-users-feel-heard","Step 7: Let Your Users Feel Heard",[15,1194,1195],{},"In your components, bring the translations to life:",[15,1197,1198],{},[65,1199,1200],{},"Client Component",[144,1202,1204],{"className":918,"code":1203,"language":920,"meta":20,"style":20},"import {useTranslations} from 'next-intl';\nimport {Link} from '@\u002Fi18n\u002Fnavigation';\n\nexport default function HomePage() {\n  const t = useTranslations('HomePage');\n  return (\n    \u003Cdiv>\n      \u003Ch1>{t('title')}\u003C\u002Fh1>\n      \u003CLink href=\"\u002Fabout\">{t('about')}\u003C\u002FLink>\n    \u003C\u002Fdiv>\n  );\n}\n",[150,1205,1206,1219,1233,1237,1251,1271,1277,1286,1311,1340,1348,1352],{"__ignoreMap":20},[102,1207,1208,1210,1213,1215,1217],{"class":154,"line":155},[102,1209,294],{"class":293},[102,1211,1212],{"class":218}," {useTranslations} ",[102,1214,300],{"class":293},[102,1216,934],{"class":162},[102,1218,306],{"class":218},[102,1220,1221,1223,1226,1228,1231],{"class":154,"line":222},[102,1222,294],{"class":293},[102,1224,1225],{"class":218}," {Link} ",[102,1227,300],{"class":293},[102,1229,1230],{"class":162}," '@\u002Fi18n\u002Fnavigation'",[102,1232,306],{"class":218},[102,1234,1235],{"class":154,"line":232},[102,1236,326],{"emptyLinePlaceholder":325},[102,1238,1239,1241,1243,1245,1248],{"class":154,"line":247},[102,1240,366],{"class":293},[102,1242,369],{"class":293},[102,1244,979],{"class":293},[102,1246,1247],{"class":158}," HomePage",[102,1249,1250],{"class":218},"() {\n",[102,1252,1253,1255,1258,1260,1263,1265,1268],{"class":154,"line":258},[102,1254,1059],{"class":293},[102,1256,1257],{"class":225}," t",[102,1259,343],{"class":293},[102,1261,1262],{"class":158}," useTranslations",[102,1264,755],{"class":218},[102,1266,1267],{"class":162},"'HomePage'",[102,1269,1270],{"class":218},");\n",[102,1272,1273,1275],{"class":154,"line":264},[102,1274,1108],{"class":293},[102,1276,1111],{"class":218},[102,1278,1279,1281,1284],{"class":154,"line":474},[102,1280,1117],{"class":218},[102,1282,1283],{"class":1120},"div",[102,1285,1141],{"class":218},[102,1287,1288,1290,1293,1296,1299,1301,1304,1307,1309],{"class":154,"line":479},[102,1289,1135],{"class":218},[102,1291,1292],{"class":1120},"h1",[102,1294,1295],{"class":218},">{",[102,1297,1298],{"class":158},"t",[102,1300,755],{"class":218},[102,1302,1303],{"class":162},"'title'",[102,1305,1306],{"class":218},")}\u003C\u002F",[102,1308,1292],{"class":1120},[102,1310,1141],{"class":218},[102,1312,1313,1315,1317,1320,1322,1325,1327,1329,1331,1334,1336,1338],{"class":154,"line":485},[102,1314,1135],{"class":218},[102,1316,552],{"class":225},[102,1318,1319],{"class":158}," href",[102,1321,789],{"class":293},[102,1323,1324],{"class":162},"\"\u002Fabout\"",[102,1326,1295],{"class":218},[102,1328,1298],{"class":158},[102,1330,755],{"class":218},[102,1332,1333],{"class":162},"'about'",[102,1335,1306],{"class":218},[102,1337,552],{"class":225},[102,1339,1141],{"class":218},[102,1341,1342,1344,1346],{"class":154,"line":494},[102,1343,1173],{"class":218},[102,1345,1283],{"class":1120},[102,1347,1141],{"class":218},[102,1349,1350],{"class":154,"line":850},[102,1351,1183],{"class":218},[102,1353,1354],{"class":154,"line":856},[102,1355,267],{"class":218},[15,1357,1358],{},[65,1359,1360],{},"Server Component",[144,1362,1364],{"className":918,"code":1363,"language":920,"meta":20,"style":20},"import {getTranslations} from 'next-intl\u002Fserver';\n\nexport default async function HomePage() {\n  const t = await getTranslations('HomePage');\n  return \u003Ch1>{t('title')}\u003C\u002Fh1>;\n}\n",[150,1365,1366,1380,1384,1398,1417,1441],{"__ignoreMap":20},[102,1367,1368,1370,1373,1375,1378],{"class":154,"line":155},[102,1369,294],{"class":293},[102,1371,1372],{"class":218}," {getTranslations} ",[102,1374,300],{"class":293},[102,1376,1377],{"class":162}," 'next-intl\u002Fserver'",[102,1379,306],{"class":218},[102,1381,1382],{"class":154,"line":222},[102,1383,326],{"emptyLinePlaceholder":325},[102,1385,1386,1388,1390,1392,1394,1396],{"class":154,"line":232},[102,1387,366],{"class":293},[102,1389,369],{"class":293},[102,1391,976],{"class":293},[102,1393,979],{"class":293},[102,1395,1247],{"class":158},[102,1397,1250],{"class":218},[102,1399,1400,1402,1404,1406,1408,1411,1413,1415],{"class":154,"line":247},[102,1401,1059],{"class":293},[102,1403,1257],{"class":225},[102,1405,343],{"class":293},[102,1407,792],{"class":293},[102,1409,1410],{"class":158}," getTranslations",[102,1412,755],{"class":218},[102,1414,1267],{"class":162},[102,1416,1270],{"class":218},[102,1418,1419,1421,1424,1426,1428,1430,1432,1434,1436,1438],{"class":154,"line":258},[102,1420,1108],{"class":293},[102,1422,1423],{"class":218}," \u003C",[102,1425,1292],{"class":1120},[102,1427,1295],{"class":218},[102,1429,1298],{"class":158},[102,1431,755],{"class":218},[102,1433,1303],{"class":162},[102,1435,1306],{"class":218},[102,1437,1292],{"class":1120},[102,1439,1440],{"class":218},">;\n",[102,1442,1443],{"class":154,"line":264},[102,1444,267],{"class":218},[70,1446,1448],{"id":1447},"not-every-conversation-starts-with-a-word","Not Every Conversation Starts With a Word",[15,1450,1451,1452,1455],{},"Sometimes, the most thoughtful form of listening is quiet observation. Instead of putting the language in the URL, we let the app detect the user's preferences and adapt silently. This is where ",[65,1453,1454],{},"listening without i18n routing"," shines — cleaner URLs, language chosen by the user, and a more fluid experience for apps where explicit routing isn't needed.",[15,1457,1458,1459],{},"If that approach sounds more like the kind of experience you want to create, next-intl supports it beautifully. You can explore that approach here:\n👉 ",[53,1460,1463],{"href":1461,"rel":1462},"https:\u002F\u002Fnext-intl.dev\u002Fdocs\u002Fgetting-started\u002Fapp-router\u002Fwithout-i18n-routing",[57],"Listening Without i18n Routing →",[70,1465,1467],{"id":1466},"speak-their-language-feel-their-world","Speak Their Language, Feel Their World",[15,1469,1470],{},"In the end, being heard is about more than just the words we say — it's about how we make people feel. Whether it's through visible cues like URLs or quiet detection of preferences, the heart of internationalization is empathy. It's about designing with the listener in mind.",[15,1472,1473],{},"With next-intl and Next.js, you're not just building apps — you're creating spaces that speak to people, in their language, on their terms.",[15,1475,1476],{},"And that's the kind of conversation worth having.",[1478,1479,1480],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":20,"searchDepth":222,"depth":222,"links":1482},[1483,1484,1488,1489,1498,1499],{"id":12,"depth":232,"text":13},{"id":72,"depth":222,"text":73,"children":1485},[1486,1487],{"id":85,"depth":232,"text":86},{"id":114,"depth":232,"text":115},{"id":135,"depth":222,"text":136},{"id":179,"depth":222,"text":180,"children":1490},[1491,1492,1493,1494,1495,1496,1497],{"id":195,"depth":232,"text":196},{"id":270,"depth":232,"text":271},{"id":377,"depth":232,"text":378},{"id":589,"depth":232,"text":590},{"id":679,"depth":232,"text":680},{"id":911,"depth":232,"text":912},{"id":1191,"depth":232,"text":1192},{"id":1447,"depth":222,"text":1448},{"id":1466,"depth":222,"text":1467},"https:\u002F\u002Fiamlope.medium.com\u002Fbeing-heard-isnt-just-about-speaking-it-s-about-understanding-b700929c5743","2025-05-26","A guide to internationalization (i18n) in Next.js using next-intl to build multilingual, user-aware web experiences.",false,"md",null,{},"\u002Fblog\u002Fbeing-heard","6min",{"title":5,"description":1502},"blog\u002Fbeing-heard","BjXWRPicNvxmZuCMIRswV1C75TBXDzkCX3Odqr97uJg",1781421037286]